#include "ViewChangeRow.hpp"

using namespace BFT::order::statemanagement;

ViewChangeRow::ViewChangeRow()
{
	smallestView = 0;
	lastReceived = 0;
	lastRemoved = 0;
	viewChanges.resize(BFT::Parameters::getOrderCount());
	pendingAcks.resize(BFT::Parameters::getOrderCount());
}



//Add a view change to the row.

//the view change is only added if it is for a view larger than
//the current view held for the specified new primary.

//After adding a view change message, it pulls over any pending
//vcacks as aprpopriate.

//Updates the smallestView value as appropriate.

void ViewChangeRow::observeViewChange(boost::shared_ptr<BFT::order::messages::ViewChange> _vc, bool _amprimary)
{
	int vindex = index(_vc.get()->getView());
	
	if (_vc.get()->getView() < lastReceived)
		return;
	
	lastReceived = _vc.get()->getView();
	
	if ( viewChanges.at(vindex).use_count() == 0 ||
	     viewChanges.at(vindex).get()->getView() < _vc.get()->getView() )
	{
		viewChanges.at(vindex).reset( new ViewChangeCertificate(_vc.get()->getView(), _amprimary) );
	}
	
	if (viewChanges.at(vindex).get()->getViewChange().use_count() != 0)
	{
		//already have a view change
		return;
	}
	
	viewChanges.at(vindex).get()->observeViewChange(_vc);
	
	for (unsigned int i = 0; i< pendingAcks.size(); i++)
	{
		if ( pendingAcks.at(i).use_count()!=0 &&
		     pendingAcks.at(i).get()->getViewNo() == _vc.get()->getView())
		{
			viewChanges.at(vindex).get()->addAck(pendingAcks.at(i));
			pendingAcks.at(i).reset();
		}     
	}
	
	if (smallestView == lastRemoved || _vc.get()->getView() < smallestView )
	{
		smallestView = _vc.get()->getView();
	}	
}

//Adds a view change Ack to the row.

//If the view change ack is for the a certificat that currently
//has a view change, then it is added to that certificate.

//If the view change ack is more recent than the last view
//change ack sent by the sending replica, then replace the old
//one and cache the current view change ack
void ViewChangeRow::addViewChangeAck(boost::shared_ptr<BFT::order::messages::ViewChangeAck> _vca)
{
	int vindex = index(_vca.get()->getViewNo());
	uint32_t send = _vca.get()->getSender();
	
	if (viewChanges.at(vindex).use_count() != 0 &&
	    viewChanges.at(vindex).get()->getView() == _vca.get()->getViewNo())
	{
		viewChanges.at(vindex).get()->addAck(_vca);
	}
	else
	{
		if (pendingAcks.at(send).use_count() == 0 ||
		    pendingAcks.at(send).get()->getViewNo() < _vca.get()->getViewNo() )
		{
			pendingAcks.at(send) = _vca;		
		}
	}
}

void ViewChangeRow::addConfirmView(boost::shared_ptr<BFT::order::messages::ConfirmView> _cv, bool _amprimary)
{
	int vindex = index(_cv.get()->getViewNo());
	if( _cv.get()->getViewNo() < lastReceived)
		return;
	if( viewChanges.at(vindex).use_count() == 0 ||
	    _cv.get()->getViewNo() > viewChanges.at(vindex).get()->getView() )
	{
		viewChanges.at(vindex).reset( new ViewChangeCertificate( _cv.get()->getViewNo(), _amprimary ) );
	}
	viewChanges.at(vindex).get()->addConfirmView(_cv);
	
	if( smallestView == lastRemoved || 
	    _cv.get()->getViewNo() > viewChanges.at(vindex).get()->getView() )
	{
		smallestView = _cv.get()->getViewNo();
	}
}

//Return view changecertificate for view.
//Returns null if no such certificate exists.
//If a non-null value is returned, then future calls will
//return null
boost::shared_ptr<ViewChangeCertificate> ViewChangeRow::getCertificate(uint32_t _view)
{
	int vindex = index(_view);

	boost::shared_ptr<ViewChangeCertificate> vc = viewChanges.at(vindex);
	if(vc.use_count() == 0 || vc.get()->getView() != _view)
		vc.reset();
	else
		viewChanges.at(vindex).reset();
		
	lastRemoved = _view;
	if(smallestView <= _view)
	{
		uint32_t tmp = _view;
		for (unsigned int i=0; i<viewChanges.size(); i++)
		{
			if (viewChanges.at(i).use_count() != 0 &&
			    ( viewChanges.at(i).get()->getView() < tmp || tmp ==_view))
			{
				if(viewChanges.at(i).get()->getView() <= smallestView)
				{
					fprintf(stderr,"ViewChangeRow::getCertificate says: something is horribly wrong\n");
					exit(159);
				}
				else
				{
					tmp = viewChanges.at(i).get()->getView();
				}
			}
		}
		
		smallestView = tmp;
	}
	
	return vc;
}


